package com.yyt.database_demo.interceptor;

import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;

import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

import com.yyt.database_demo.utils.ReflectUtils;

/**
 * 可以拦截四种类型:
 * 1.Executor：拦截执行器的方法。
 * 2.ParameterHandler：拦截参数的处理。
 * 3.ResultHandler：拦截结果集的处理。
 * 4.StatementHandler：拦截Sql语法构建的处理。
 *
 * Intercepts注解需要一个Signature(拦截点)参数数组。
 * 通过Signature来指定拦截哪个对象里面的哪个方法,args是为了辨识方法的重载
 * 
 * @author yeyangtao created at 2022/2/23 14:08
 */
@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }),
        @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) })
public class SqlInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        Object obj = boundSql.getParameterObject();
        String sql = boundSql.getSql();

        System.out.println(obj);

        System.out.println("before:" + sql);
        sql = sql.toLowerCase();
        System.out.println("after:" + sql);

        ReflectUtils.setFieldValue(boundSql, "sql", sql);

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 判断是否拦截这个类型对象（根据@Intercepts注解决定），然后决定是返回一个代理对象还是返回原对象。
        // 故我们在实现plugin方法时，要判断一下目标类型，如果是插件要拦截的对象时才执行Plugin.wrap方法，否则的话，直接返回目标本身。
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * 拦截器需要一些变量对象，而且这个对象是支持可配置的。
     * 
     * @param properties
     *            properties
     */
    @Override
    public void setProperties(Properties properties) {

    }
}
